﻿//
// RepositoryBase.cs
//
// Authors:
//    Lasse Andreas Hauge Petersen <09547@iha.dk>
//    Søren Flohr Nielsen <10638@iha.dk>
//    Claus Jørgensen <10229@iha.dk>
//
using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;

namespace RSD.Data
{
    /// <summary>
    /// Provides a base implementation of the <see cref="IRepository&lt;T&gt;"/>.
    /// </summary>
    /// <typeparam name="T">The type of elements in the repository context.</typeparam>
    public abstract class RepositoryBase<T> : IRepository<T>
        where T : class
    {
        /// <summary>
        /// Internal IObjectContext for querying data.
        /// </summary>
        private readonly IObjectContext context;

        /// <summary>
        /// Internal IObjectSet for modifying data.
        /// </summary>
        private readonly IObjectSet<T> objectSet;

        /// <summary>
        /// Initializes a new instance of the <see cref="RepositoryBase&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public RepositoryBase(IObjectContext context)
        {
            Contract.Requires(context != null);

            this.context = context;

            if (this.context != null)
            {
                this.objectSet = context.CreateObjectSet<T>();

                if (this.objectSet == null)
                {
                    throw new InvalidOperationException(
                        "Could not create a valid ObjectSet from the supplied context");
                }
            }
        }

        [ContractInvariantMethod]
        private void Invariant()
        {
            Contract.Invariant(context != null);
            Contract.Invariant(objectSet != null);
        }

        #region IRepository<T> Members

        /// <summary>
        /// Returns a enumerator for all items in the repository context. 
        /// </summary>
        /// <returns>A enumerator for all items in the repository context.</returns>
        public virtual IEnumerable<T> FindAll()
        {
            if (objectSet != null)
            {
                return objectSet.AsEnumerable();
            }

            return Enumerable.Empty<T>();
        }

        /// <summary>
        /// Finds a sequence of values from the repository context based on a predicate.
        /// </summary>
        /// <param name="predicate">A function to test each element for a condition.</param>
        /// <returns>A sequence of values from the repository context.</returns>
        public virtual IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate)
        {
            if (objectSet != null && predicate != null)
            {
                return objectSet.Where(predicate);
            }

            return Enumerable.Empty<T>();
        }

        /// <summary>
        /// Notifies the set that an object that represents a new entity must be added to the set.
        /// </summary>
        /// <param name="entity">The new object to add to the set.</param>
        /// <returns>The <see cref="IRepository&lt;T&gt;"/> fluent interface</returns>
        public virtual IRepository<T> Add(T entity)
        {
            if (objectSet != null)
            {
                objectSet.AddObject(entity);
            }

            return this;
        }

        /// <summary>
        /// Notifies the set that an object that represents an existing entity must be deleted from the set.
        /// </summary>
        /// <param name="entity">The existing object to delete from the set.</param>
        /// <returns>The <see cref="IRepository&lt;T&gt;"/> fluent interface</returns>
        public virtual IRepository<T> Remove(T entity)
        {
            if (objectSet != null)
            {
                objectSet.DeleteObject(entity);
            }

            return this;
        }

        /// <summary>
        /// Persists all updates to the data source and resets change tracking in the repository context.
        /// </summary>
        /// <returns>The <see cref="IRepository&lt;T&gt;"/> fluent interface</returns>
        public virtual IRepository<T> SaveChanges()
        {
            if (context != null)
            {
                context.SaveChanges();
            }

            return this;
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
            if (context != null)
            {
                context.Dispose();
            }

            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
